-- icache wrapper
-- this is provided for a place holder until you do the cache labs
-- until then you should just place this file between your fetch stage
-- of your pipeline and your priority mux for main memory.
library ieee;
use ieee.std_logic_1164.all;

entity levelone is
  port(
    CLK       : in  std_logic;
    nReset    : in  std_logic;
    ramState  : in std_logic_vector(1 downto 0);
    memhalt   : in std_logic;
    cpuhalt   : out std_logic;

    iMemRead  : in  std_logic;                       -- CPU side
    iMemAddr  : in  std_logic_vector (31 downto 0);  -- CPU side
    iMemData  : out std_logic_vector (31 downto 0);  -- CPU side
    
    dMemRead       : in  std_logic;                       -- CPU side
    dMemWrite      : in  std_logic;                       -- CPU side
    dMemAddr       : in  std_logic_vector (31 downto 0);  -- CPU side
    dMemDataRead   : out std_logic_vector (31 downto 0);  -- CPU side
    dMemDataWrite  : in  std_logic_vector (31 downto 0);  -- CPU side 

    RamAddr   : out std_logic_vector(15 downto 0);
    ramData   : out std_logic_vector(31 downto 0);
    ramQ      : in std_logic_vector(31 downto 0);
    Ram_RE    : out std_logic;
    Ram_WE    : out std_logic;
    iMemWait   : out std_logic;
    dMemWait  : out std_logic
    
  );

end levelone;

architecture struct of levelone is

component icache is
  port(
    CLK       : in  std_logic;
    nReset    : in  std_logic;

    iMemRead  : in  std_logic;                       -- CPU side
    iMemWait  : out std_logic;                       -- CPU side
    iMemAddr  : in  std_logic_vector (31 downto 0);  -- CPU side
    iMemData  : out std_logic_vector (31 downto 0);  -- CPU side

    aiMemWait : in  std_logic;                       -- arbitrator side
    aiMemRead : out std_logic;                       -- arbitrator side
    aiMemAddr : out std_logic_vector (31 downto 0);  -- arbitrator side
    aiMemData : in  std_logic_vector (31 downto 0)   -- arbitrator side
    );

end component;

component dcache is
  port(
    CLK            : in  std_logic;
    nReset         : in  std_logic;

    dMemRead       : in  std_logic;                       -- CPU side
    dMemWrite      : in  std_logic;                       -- CPU side
    dMemWait       : out std_logic;                       -- CPU side
    dMemAddr       : in  std_logic_vector (31 downto 0);  -- CPU side
    dMemDataRead   : out std_logic_vector (31 downto 0);  -- CPU side
    dMemDataWrite  : in  std_logic_vector (31 downto 0);  -- CPU side

    adMemRead      : out std_logic;                       -- arbitrator side
    adMemWrite     : out std_logic;                       -- arbitrator side
    adMemWait      : in  std_logic;                       -- arbitrator side
    adMemAddr      : out std_logic_vector (31 downto 0);  -- arbitrator side
    adMemDataRead  : in  std_logic_vector (31 downto 0);  -- arbitrator side
    adMemDataWrite : out std_logic_vector (31 downto 0);   -- arbitrator side
    adhalt         : in std_logic;
    cpuhalt        : out std_logic
    );

end component;

  signal aiMemWait, aiMemRead, adMemRead, adMemWrite, adMemWait : std_logic;

  signal aiMemAddr, aiMemData, adMemAddr, adMemDataRead, adMemDataWrite : std_logic_vector(31 downto 0);
  
  type states is (IDLE, DRAIN);
  signal state, nextstate : states;
  
  signal ramready : std_logic;
  
begin

  I1 : icache 
  port map(
    CLK       => CLK,
    nReset    => nReset,

    iMemRead  => iMemRead,
    iMemWait  => iMemWait,
    iMemAddr  => iMemAddr,
    iMemData  => iMemData,

    aiMemWait => aiMemWait,
    aiMemRead => aiMemRead,
    aiMemAddr => aiMemAddr,
    aiMemData => aiMemData
    );
aiMemData <= ramQ;

  D1 : dcache 
  port map(
    CLK            => CLK,
    nReset         => nReset,

    dMemRead       => dMemRead,
    dMemWrite      => dMemWrite,
    dMemWait       => dMemWait,
    dMemAddr       => dMemAddr,
    dMemDataRead   => dMemDataRead,
    dMemDataWrite  => dMemDataWrite,

    adMemRead      => adMemRead,
    adMemWrite     => adMemWrite,
    adMemWait      => adMemWait,
    adMemAddr      => adMemAddr,
    adMemDataRead  => adMemDataRead,
    adMemDataWrite => adMemDataWrite,
    adhalt         => memhalt,
    cpuhalt        => cpuhalt
    );
    
    adMemDataRead <= ramQ;
statereg : process(CLK, nReset)
  begin
    if nReset = '0' then
      state <= IDLE;
    elsif rising_edge(CLK) then
      state <= nextstate;
    end if;
  end process statereg;
next_state : process(state, memhalt)
    begin
      
      nextstate <= IDLE;

      case state is
      
      when IDLE =>
        
        if (memhalt = '1') then
          nextstate <= DRAIN;
        else
          nextstate <= IDLE;
        end if;

        
      when DRAIN =>
        nextstate <= DRAIN;

       
        
      when OTHERS =>
        nextstate <= IDLE;
        
      end case;
    end process;




  ramready <= '0' when ramstate = "01" or ramstate = "11" else
             '1';

  ramAddr <= aiMemAddr(15 downto 0) when aiMemRead = '1' and adMemRead = '0' and adMemWrite = '0' else
            adMemAddr(15 downto 0);
            
  ramData <= adMemDataWrite;
  aiMemWait <= (not ramready and aiMemRead and not adMemRead and not adMemWrite) or ((adMemWrite or adMemRead));-- and aiMemRead);

  adMemWait <= (not ramready and (adMemWrite or adMemRead));

  Ram_WE <= '1' when (adMemWrite = '1') else
            '0';
  Ram_RE <= '1' when (aiMemRead = '1' or adMemRead = '1') and adMemWrite = '0'  and memhalt = '0' else
            '0';
end struct;

